/*
 * contrib/spi/insert_username.c
 *
 * 在触发器响应中插入用户名
 * 用法： insert_username (列名)
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/rel.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(insert_username);

Datum insert_username(PG_FUNCTION_ARGS)
{
	TriggerData *fc_trigdata = (TriggerData *) fcinfo->context;
	Trigger    *fc_trigger;		/* 获取触发器名称 */
	int			fc_nargs;			/* 参数数量 */
	Datum		fc_newval;			/* 列的新值 */
	bool		fc_newnull;		/* 空标志 */
	char	  **fc_args;			/* 参数 */
	char	   *fc_relname;		/* 触发的关系名称 */
	Relation	fc_rel;			/* 触发的关系 */
	HeapTuple	fc_rettuple = NULL;
	TupleDesc	fc_tupdesc;		/* 元组描述 */
	int			fc_attnum;

	/* 来自 autoinc.c 的合理性检查 */
	if (!CALLED_AS_TRIGGER(fcinfo))
		/* 内部错误 */
		elog(ERROR, "insert_username: not fired by trigger manager");
	if (!TRIGGER_FIRED_FOR_ROW(fc_trigdata->tg_event))
		/* 内部错误 */
		elog(ERROR, "insert_username: must be fired for row");
	if (!TRIGGER_FIRED_BEFORE(fc_trigdata->tg_event))
		/* 内部错误 */
		elog(ERROR, "insert_username: must be fired before event");

	if (TRIGGER_FIRED_BY_INSERT(fc_trigdata->tg_event))
		fc_rettuple = fc_trigdata->tg_trigtuple;
	else if (TRIGGER_FIRED_BY_UPDATE(fc_trigdata->tg_event))
		fc_rettuple = fc_trigdata->tg_newtuple;
	else
		/* 内部错误 */
		elog(ERROR, "insert_username: cannot process DELETE events");

	fc_rel = fc_trigdata->tg_relation;
	fc_relname = SPI_getrelname(fc_rel);

	fc_trigger = fc_trigdata->tg_trigger;

	fc_nargs = fc_trigger->tgnargs;
	if (fc_nargs != 1)
		/* 内部错误 */
		elog(ERROR, "insert_username (%s): one argument was expected", fc_relname);

	fc_args = fc_trigger->tgargs;
	fc_tupdesc = fc_rel->rd_att;

	fc_attnum = SPI_fnumber(fc_tupdesc, fc_args[0]);

	if (fc_attnum <= 0)
		ereport(ERROR,
				(errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
				 errmsg("\"%s\" has no attribute \"%s\"", fc_relname, fc_args[0])));

	if (SPI_gettypeid(fc_tupdesc, fc_attnum) != TEXTOID)
		ereport(ERROR,
				(errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
				 errmsg("attribute \"%s\" of \"%s\" must be type TEXT",
						fc_args[0], fc_relname)));

	/* 创建包含名称的字段 */
	fc_newval = CStringGetTextDatum(GetUserNameFromId(GetUserId(), false));
	fc_newnull = false;

	/* 构造新元组 */
	fc_rettuple = heap_modify_tuple_by_cols(fc_rettuple, fc_tupdesc,
										 1, &fc_attnum, &fc_newval, &fc_newnull);

	pfree(fc_relname);

	return PointerGetDatum(fc_rettuple);
}
